using Ruitu.Edu.TrainingDemo.Models;
using Ruitu.Edu.TrainingDemo.ViewModels;
using Microsoft.AspNetCore.Html;
using OrchardCore.DisplayManagement.Descriptors;
using System.Threading.Tasks;

namespace Ruitu.Edu.TrainingDemo.Services;

/// <summary>
/// 形状隐藏形状表提供者 - 演示如何钩入形状生命周期
/// 
/// 主要用途：
/// - 演示如何有条件地隐藏形状
/// - 展示形状表提供者的实现
/// - 演示形状事件的处理
/// 
/// 使用场景：
/// - 需要根据条件隐藏特定形状
/// - 需要自定义形状显示逻辑
/// - 需要操作形状元数据
/// 
/// 设计原因：
/// - 利用 Orchard Core 的形状表机制
/// - 提供灵活的形状控制
/// - 支持复杂的显示逻辑
/// 
/// 设计模式：
/// - 提供者模式：实现 IShapeTableProvider 接口
/// - 事件模式：使用 OnDisplaying 事件
/// - 策略模式：根据条件应用不同的显示策略
/// </summary>
// 现在您已经学到了很多关于形状的技巧，让我们看看如何钩入它们的生命周期。一个常见的任务
// 是有条件地隐藏形状。通常，您会为此使用一些内置的放置机制（请参阅：
// https://docs.orchardcore.net/en/latest/docs/reference/modules/Placements/）但有时您只需要应用一些
// 自定义逻辑。您如何做到这一点？通过实现 IShapeTableProvider！
internal sealed class ShapeHidingShapeTableProvider : IShapeTableProvider
{
    /// <summary>
    /// 发现形状表构建器
    /// </summary>
    /// <param name="builder">形状表构建器</param>
    /// <returns>完成的任务</returns>
    // 整个事情从 Discover() 开始，然后您可以使用 Describe() 定位特定形状。隐藏形状
    // 可以通过下面演示的两种方式之一发生，但您不需要两者。
    public ValueTask DiscoverAsync(ShapeTableBuilder builder)
    {
        builder
            .Describe(nameof(PersonPart))
            // 一个选项是从 OnDisplaying 事件覆盖形状的输出。在这里您有完整的上下文
            // 可用，比如内容部分和内容项用于部分的显示形状，就像 PersonPart 一样。但是，在
            // 这一点上，形状的工厂已经被执行，因此如果它计算缓慢（比如它从慢速 Web 服务获取数据），
            // 那么我们已经等待了它。
            .OnDisplaying(displaying =>
            {
                // 查看如果将 Person Page 的名称更改为包含单词 "Hidden" 然后在前端查看会发生什么！
                if (displaying.Shape is PersonPartViewModel viewModel && viewModel.Name.Contains("Hidden"))
                {
                    displaying.ChildContent = HtmlString.Empty;
                }

                // 您还可以在此类事件中做更多事情，例如通过 displaying.Shape.Metadata 更改形状的元数据
                // （请参阅：https://docs.orchardcore.net/en/latest/docs/reference/core/Placement/#manipulating-shape-metadata），
                // 比如添加自定义包装器或替代项（https://docs.orchardcore.net/en/latest/docs/reference/modules/Themes/#alternates）。
            })
            // 另一个选项是覆盖形状的放置，完全禁用形状。这样，它的工厂也不会被执行。
            // 但是，可用的上下文很少。我们在这里定位 "Hidden" DisplayType，否则我们所有的 PersonParts 都会被隐藏。
            .Placement(context => context.DisplayType == "Hidden" ? new PlacementInfo { Location = "-" } : null);

        return ValueTask.CompletedTask;
    }

    // 也可以通过 IPlacementNodeFilterProvider 和 IPlacementInfoResolver 实现扩展 placement.json 文件处理。
    // 如果您感兴趣，请在 Orchard 源代码中查看它们！另外，如果您只对形状事件感兴趣，
    // 您可以实现 IShapeDisplayEvents。
}

// 训练部分结束：形状表

// 下一站：Controllers/CacheController.cs
